const express = require("express");
const router = express.Router();

// 要支持上下文，背后的原理非常简单：
// 拿一个数组来存储会话的历史记录，之后每一次会将历史会话记录一同发给大模型
const conversations = []; // 该数组存储会话记录
/**
 * conversations = [
 *  {role: "user", content: "你是谁"},
 *  {role: "assistant", content: "大模型的回复"},
 * ]
 */

// 注意，需要是一个post请求
router.post("/ask", async (req, res) => {
  // 拿到用户的问题
  const question = req.body.question || "";

  // 每一次 prompt 会将历史会话带过去
  const prompt = [
    "你是一个中文智能助手，请使用中文回答用户的问题。",
    // 历史记录
    ...conversations.map(
      (item) => `${item.role === "user" ? "用户" : "助手"}：${item.content}`
    ),
    `用户的问题：${question}`,
  ].join("\n");

  const response = await fetch("http://localhost:11434/api/generate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "llama3",
      prompt,
      stream: true,
    }),
  });

  // 首先需要设置一下响应头
  // 告诉客户端这是一个 Server-Sent-Events 流（SSE）
  res.setHeader("Content-Type", "text/event-stream");
  // 禁用缓存，确保每一次获取到的是最新的内容，防止浏览器缓存 SSE 内容
  res.setHeader("Cache-Control", "no-cache");

  const reader = response.body.getReader(); // 拿到 Reader 对象
  const decoder = new TextDecoder("utf-8"); // 创建一个 utf-8 的解码器

  let fullResponse = ""; // 用于存储模型这一次的完整回复

  while (true) {
    const { done, value } = await reader.read(); // 读取当前块的内容
    if (done) break;

    // 对二进制数据进行解码
    const chunk = decoder.decode(value, { stream: true });
    // chunk = '{"response":"你好"}\n{"response":"，"}\n'

    // 后面就是一些 JS 相关的基操了
    const lines = chunk.split("\n").filter((line) => line.trim());
    // lines = [
    //   '{"response":"你好"}',
    //   '{"response":"，"}',
    //   '{"response":"我是"}',
    //   '{"response":"AI助手"}'
    // ]
    for (const line of lines) {
      try {
        const data = JSON.parse(line); // data = {"response":"你好"}
        if (data.response) {
          fullResponse += data.response; // 拼接这一次的回答 token 到完整的回复里面
          // 发送给客户端
          res.write(`${JSON.stringify({ response: data.response })}\n`);
        }
      } catch (e) {
        console.error("JSON解析失败☹️", e.message);
      }
    }
  }

  // 将这一次会话记录到历史数组里面
  conversations.push(
    { role: "user", content: question },
    { role: "assistant", content: fullResponse }
  );

  // 限制会话历史的长度，仅保留最近的 20 条消息
  // 在 ChatGPT 或者 Deepseek 这类型的应用中，裁剪历史会话的长度前，会会对之前的会话做一个总结
  if (conversations.length > 20)
    conversations.splice(0, conversations.length - 20);

  // 代码来到这里，说明所有的块儿都处理完了
  res.end();
});

// 用户看到会话的历史记录
router.get("/history", function (req, res) {
  res.json({
    conversations,
  });
});

// 清空会话记录
router.post("/clear", function (req, res) {
  conversations.length = 0;
  res.json({
    message: "对话历史已经清空",
  });
});

module.exports = router;
